package org.cogaen.demo.view;

import java.awt.event.KeyEvent;

import org.cogaen.box2d.EntityMovedEvent;
import org.cogaen.core.Core;
import org.cogaen.demo.entity.BulletEntity;
import org.cogaen.demo.entity.ShipEntity;
import org.cogaen.demo.state.GameDemoState;
import org.cogaen.entity.EntityCreatedEvent;
import org.cogaen.entity.EntityDestroyedEvent;
import org.cogaen.event.Event;
import org.cogaen.event.EventListener;
import org.cogaen.event.EventManager;
import org.cogaen.event.SimpleEvent;
import org.cogaen.input.KeyPressedEvent;
import org.cogaen.input.KeyboardControllerSource;
import org.cogaen.java2d.Camera;
import org.cogaen.java2d.ImageHandle;
import org.cogaen.java2d.SceneManager;
import org.cogaen.java2d.SceneNode;
import org.cogaen.java2d.SpriteHandle;
import org.cogaen.logging.LoggingService;
import org.cogaen.math.EaseInOut;
import org.cogaen.resource.ResourceManager;
import org.cogaen.task.AbstractTask;
import org.cogaen.task.TaskManager;

public class GameDemoView implements EventListener {
	
	/** World width to be displayed on screen in meters. */
	private static final double VIEW_WORLD_WIDTH = 38.0;
	
	/** Used to identify source of logging messages. */
	private static final String LOGGING_SOURCE = "GameDemoView";
	
	private static final double CAMERA_EASE_TIME = 0.3;
	
	private Core core;
	private SceneManager scnMngr;
	private EventManager evtMngr;
	private UpsHud upsHud;
	private Camera camera;
	private CameraMover cameraMover;
	private KeyboardControllerSource keyboardController;
		
	public GameDemoView(Core core) {
		this.core = core;
		this.upsHud = new UpsHud(this.core);
		this.scnMngr = SceneManager.getInstance(this.core);
		this.evtMngr = EventManager.getInstance(this.core);
		this.keyboardController = new KeyboardControllerSource(this.core, GameDemoState.PLAYER_ENTITY);
		this.keyboardController.addAction(KeyEvent.VK_SPACE);		
	}
	
	public void registerResources(String resourceGroup) {
		ResourceManager resMngr = ResourceManager.getInstance(this.core);
		
		resMngr.addResource( resourceGroup, new ImageHandle("level1_background_img", "level1_1024x768.jpg") );
		resMngr.addResource( resourceGroup, new SpriteHandle("level1_background_spr", "level1_background_img", 48.0, 36.0) );
		resMngr.addResource( resourceGroup, new ImageHandle("ship_img", "ship_64x64.png") );
		resMngr.addResource( resourceGroup, new SpriteHandle("ship_spr", "ship_img", 2.1, 2.1) );				
	}
	
	public void engage() {
		// register for relevant events
		EventManager evtMngr = EventManager.getInstance( this.core );		
		evtMngr.addListener(this, EntityCreatedEvent.TYPE);
		evtMngr.addListener(this, EntityDestroyedEvent.TYPE);
		evtMngr.addListener(this, EntityMovedEvent.TYPE);
		evtMngr.addListener(this, KeyPressedEvent.TYPE);
		
		// setup keyboard controller
		this.keyboardController.engage();

		// set up graphical representation
		this.scnMngr.setClearBackground(true);
		this.scnMngr.getScreen().hideMouseCursor();
		this.scnMngr.getRootSceneNode().addVisual( scnMngr.createSpriteVisual("level1_background_spr") );
		
		this.camera = scnMngr.createCamera();
		this.camera.setZoom( scnMngr.getScreen().getWidth() / VIEW_WORLD_WIDTH);								
		this.cameraMover = new CameraMover(this.core, this.camera, CAMERA_EASE_TIME);
		TaskManager.getInstance(this.core).attachTask(this.cameraMover);
				
		// hud
		this.upsHud.engage();
	}
	
	public void disengage() {		
		this.upsHud.disengage();
		this.cameraMover.kill();
		this.scnMngr.destroyAll();
		this.scnMngr.getScreen().showMouseCursor();		
		this.keyboardController.disengage();
		EventManager.getInstance(this.core).removeListener(this);
		
		this.camera = null;
	}

	@Override
	public void handleEvent(Event event) {
		if (event.getType().equals( EntityMovedEvent.TYPE) ) {
			EntityMovedEvent entityMoved = (EntityMovedEvent) event;
			moveEntity(entityMoved.getName(), entityMoved.getX(), entityMoved.getY(), entityMoved.getAngle());
			
		} else if (event.getType().equals( EntityCreatedEvent.TYPE) ) {
			EntityCreatedEvent entityCreated = (EntityCreatedEvent) event;
			createRepresentation(entityCreated.getEntityName(), entityCreated.getEntityType());
			
		} else if (event.getType().equals( EntityDestroyedEvent.TYPE) ) {
			EntityDestroyedEvent entityDestroyed = (EntityDestroyedEvent) event;
			destroyRepresentation(entityDestroyed.getEntityName());
			
		} else if (event.getType().equals(KeyPressedEvent.TYPE) ) {
			KeyPressedEvent keyPressed = (KeyPressedEvent) event;
			handleKeyPressedEvent( keyPressed.getKeyCode() );
		}
	}

	private void destroyRepresentation(String name) {
		this.scnMngr.destroySceneNode(name);
	}

	private void createRepresentation(String name, String entityType) {
		if (entityType.equals(ShipEntity.TYPE)) {
			createShipRepresentation(name);
		} else if (entityType.equals(BulletEntity.TYPE)) {
			createBulletRepresentation(name);
		}
	}

	private void createBulletRepresentation(String name) {
		SceneNode node = this.scnMngr.createSceneNode(name);
		node.addVisual( this.scnMngr.createCircleVisual(BulletEntity.RADIUS) );
		this.scnMngr.getRootSceneNode().addChild(node);		
	}

	private void createShipRepresentation(String name) {
		SceneNode node = this.scnMngr.createSceneNode(name);
		node.addVisual( this.scnMngr.createSpriteVisual("ship_spr") );
		this.scnMngr.getRootSceneNode().addChild(node);		
	}

	private void moveEntity(String name, double x, double y, double angle) {
		SceneNode node = this.scnMngr.getSceneNode(name);
		if (node != null) {
			node.setPose(x, y, angle);
			if (name.equals(GameDemoState.PLAYER_ENTITY)) {
				moveCamera(x, y, angle);
			}
		} else {
			LoggingService.getInstance(this.core).logError(LOGGING_SOURCE, "MoveEntity event for unknown entity: " + name);
		}
	}
	
	private void moveCamera(double x, double y, double angle) {
		double aspectRatio = (double) this.scnMngr.getScreen().getHeight() / (double) this.scnMngr.getScreen().getWidth();
		double hBound = GameDemoState.WORLD_WIDTH / 2 - VIEW_WORLD_WIDTH / 2;
		double vBound = GameDemoState.WORLD_HEIGHT / 2 - VIEW_WORLD_WIDTH * aspectRatio / 2;
		
		if (x < -hBound) {
			x = -hBound;
		} else if (x > hBound) {
			x = hBound;
		}
		
		if (y < -vBound) {
			y = -vBound;
		} else if (y > vBound) {
			y = vBound;
		}
		
		this.cameraMover.setTargetPosition(x, y);
	}
	
	private void handleKeyPressedEvent(int keyCode) {
		switch (keyCode) {					
		case KeyEvent.VK_ESCAPE:
			this.evtMngr.enqueueEvent( new SimpleEvent(GameDemoState.END_OF_GAME) );
			break;
			
		case KeyEvent.VK_Q:
			this.scnMngr.setHighQuality( !this.scnMngr.isHighQuality() );
			break;
		}
	}

	public static class CameraMover extends AbstractTask {

		public static final String NAME = "CameraMover";
		private static final int X = 0;
		private static final int Y = 1;
		private static final int ANGLE = 2;
		
		private Camera camera;
		private EaseInOut easeInOut[];
		
		public CameraMover(Core core, Camera camera, double easeTime) {
			super(core, NAME);
			this.camera = camera;
			this.easeInOut = new EaseInOut[4];
			this.easeInOut[X] = new EaseInOut(0.0, easeTime);
			this.easeInOut[Y] = new EaseInOut(0.0, easeTime);
			this.easeInOut[ANGLE] = new EaseInOut(0.0, easeTime);
		}
		
		public void setTargetPosition(double x, double y) {
			this.easeInOut[X].setTargetValue(x);
			this.easeInOut[Y].setTargetValue(y);
		}
		
		public void setTargetPose(double x, double y, double angle) {
			this.easeInOut[X].setTargetValue(x);
			this.easeInOut[Y].setTargetValue(y);
			this.easeInOut[ANGLE].setTargetValue(angle);			
		}

		@Override
		public void update() {
			double dt = getCore().getDeltaTime();
			this.easeInOut[X].update(dt);
			this.easeInOut[Y].update(dt);
			this.easeInOut[ANGLE].update(dt);
			this.camera.setPose(this.easeInOut[X].getCurrentValue(), this.easeInOut[Y].getCurrentValue(), this.easeInOut[ANGLE].getCurrentValue());
		}
		
	}
	
}
